﻿namespace WcfComposition
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition.Primitives;
    using System.Diagnostics.Contracts;
    using System.ServiceModel.Web;

    /// <summary>
    /// Composition host build out to provide MEF composition services for RESTful web services.
    /// </summary>
    /// <remarks>Inherits from <see cref="WebServiceHost"/>.</remarks>
    public class ComposingWebServiceHost : WebServiceHost
    {
        #region Fields

        private CompositionContainer container;
        private readonly Type serviceType;

        #endregion

        #region Methods

        /// <summary>
        /// Creates and loads the composition container for the service host.
        /// </summary>
        protected virtual void Compose()
        {
            Contract.Ensures( Contract.Result<CompositionContainer>() != null );

            var catalogs = new List<ComposablePartCatalog>();
            var setup = AppDomain.CurrentDomain.SetupInformation;

            if ( setup != null )
            {
                catalogs.Add( new DirectoryCatalog( setup.ApplicationBase ) );

                if ( !string.IsNullOrEmpty( setup.PrivateBinPath ) )
                    catalogs.Add( new DirectoryCatalog( setup.PrivateBinPath ) );
            }

            var catalog = new AggregateCatalog( catalogs );

            this.container = new CompositionContainer( catalog );

            this.LoadCompositionBehavior();
        }

        /// <summary>
        /// Creates and loads the composing service behavior into the service host.
        /// </summary>
        protected virtual void LoadCompositionBehavior()
        {
            this.Description.Behaviors.Add( new ComposingServiceBehavior( this.serviceType, this.container ) );
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of the ComposingWebServiceHost, and ensuring
        /// that provided configuration elements are valid.
        /// </summary>
        /// <param name="type">A <see cref="Type"/> object.</param>
        /// <param name="uri">An array of <see cref="Uri"/> object.</param>
        public ComposingWebServiceHost( Type type, Uri[] addresses )
            : base( type, addresses )
        {
            Contract.Ensures( type != null );
            Contract.Ensures( addresses != null );

            this.serviceType = type;

            this.Compose();
        }

        #endregion

        #region Overrides

        protected override void OnClosing()
        {
            if ( this.container != null )
            {
                this.container.Dispose();
                this.container = null;
            }

            base.OnClosing();
        }

        #endregion
    }
}
